#ifndef __GMP_H__
#include <gmp.h>
#endif

#ifndef _NTH_LIB_H
#include <nth-lib.h>
#endif

#ifndef _STDINT_H
#include <stdint.h>
#endif

#ifndef _STDIO_H
#include <stdio.h>
#endif

#ifndef _STRING_H
#include <string.h>
#endif

/* Definitions begin here. */

int Clean_Nth_Plugin(
	struct Nth_Plugin * nth_plugin
){
	if (!nth_plugin){
		return NTH_ERROR_NULL_POINTER;
	}

	if (nth_plugin->plugin_name){
		free(nth_plugin->plugin_name);
		nth_plugin->plugin_name = 0;
	}

	if (nth_plugin->plugin_description){
		free(nth_plugin->plugin_description);
		nth_plugin->plugin_description = 0;
	}

	return 0;
}

int Initialize_Nth_Plugin(
	struct Nth_Plugin * nth_plugin
){
	char plugin_name[] = "number";
	char plugin_desc[] = "Read a number into slot from command line";

	if (!nth_plugin){
		return NTH_ERROR_NULL_POINTER;
	}

	nth_plugin->plugin_name = malloc(sizeof(plugin_name));

	if (!nth_plugin->plugin_name){
		return NTH_ERROR_NO_MEMORY;
	}

	memcpy(
		nth_plugin->plugin_name,
		plugin_name,
		sizeof(plugin_name)
	);

	nth_plugin->plugin_description = malloc(sizeof(plugin_desc));

	if (!nth_plugin->plugin_description){
		free(nth_plugin->plugin_name);
		nth_plugin->plugin_name = 0;
		return NTH_ERROR_NO_MEMORY;
	}

	memcpy(
		nth_plugin->plugin_description,
		plugin_desc,
		sizeof(plugin_desc)
	);

	nth_plugin->plugin_callback = Nth_Plugin_Callback;

	return 0;
}

int Nth_Plugin_Callback(
	struct Nth_Program * nth_program,
	struct Nth_Number * plugin_number
){
	int error = 0;

	size_t bin_diff = 0;
	size_t dec_diff = 0;
	size_t hex_diff = 0;
	size_t oct_diff = 0;

	char * number_str = 0;
	char * numerical_base_str = 0;
	size_t numerical_base = 0;

	__mpz_struct mpz_cast;

	if (!nth_program){
		return 1;
	}

	error = Get_Next_Nth_Argument(
		nth_program,
		&numerical_base_str
	);

	if (error){

		Push_Nth_Failure(
			nth_program,
			"Nth_Plugin_Callback",
			"Failed to get numerical base"
		);

		return 1;
	}

	bin_diff = strcmp("bin", numerical_base_str);
	dec_diff = strcmp("dec", numerical_base_str);
	hex_diff = strcmp("hex", numerical_base_str);
	oct_diff = strcmp("oct", numerical_base_str);

	if (bin_diff == 0){

		numerical_base = 2;

	} else if (dec_diff == 0){

		numerical_base = 10;

	} else if (hex_diff == 0){

		numerical_base = 16;

	} else if (oct_diff == 0){

		numerical_base = 8;

	} else {

		Push_Nth_Failure(
			nth_program,
			"Nth_Plugin_Callback",
			"Unrecognized numerical base"
		);

		return 1;
	}

	error = Iterate_Nth_Argument(nth_program);

	if (error){

		Push_Nth_Failure(
			nth_program,
			"Nth_Plugin_Callback",
			"Failed to iterate numerical base argument"
		);

		return 1;
	}

	error = Get_Next_Nth_Argument(
		nth_program,
		&number_str
	);

	if (error){

		Push_Nth_Failure(
			nth_program,
			"Nth_Plugin_Callback",
			"Failed to get number string"
		);

		return 1;
	}

	error = Iterate_Nth_Argument(nth_program);

	if (error){

		Push_Nth_Failure(
			nth_program,
			"Nth_Plugin_Callback",
			"Failed to iterate numerical value argument"
		);

		return 1;
	}

	memset(&mpz_cast, 0, sizeof(mpz_cast));

	error = mpz_init_set_str(
		&mpz_cast,
		number_str,
		numerical_base
	);

	if (error){

		Push_Nth_Failure(
			nth_program,
			"Nth_Plugin_Callback",
			"Failed to get number string"
		);

		return 1;
	}

	plugin_number->number_buffer = mpz_cast._mp_d;
	plugin_number->number_length = 0;
	plugin_number->number_length += mpz_cast._mp_size;
	plugin_number->number_length *= sizeof(mp_ptr);

	return 0;
}

